﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Ndo.Maths.MOP
{
    public class DMEA<Individual, Vector> : NSGA2<Individual, Vector>
        where Individual : XY<Vector, Vector>, new()
        where Vector : class, IList<double>, ICloneable
    {
        public DMEA()
        {
            Selection = DominationMemerySelection;
            Evaluation = new GradientCrowdingEvaluation<Vector>(GradientFunctions.Factorial, Measure.EuclideanDistance);
        }


        public Dictionary<Individual, double> Memory = new Dictionary<Individual, double>();
        public IList<Individual> DominationMemerySelection(IList<Individual> inds, int limit)
        {
            int n = inds.Count;
            Individual[] list = new Individual[n];
            inds.CopyTo(list, 0);
            if (limit >= n)
                return list;
            int[] S = new int[n], Q = new int[n];
            double[] R = new double[n];
            bool[,] bs = new bool[n, n];
            for (int i = 0; i < n; i++)
            {
                Vector v1 = inds[i].Y;
                for (int j = 0; j < i; j++)
                {
                    if (bs[j, i] = Domination(inds[j].Y, v1) == DominationTypes.Dominate)
                    {
                        S[i]++;
                        Q[j]++;
                    }
                    if(bs[i,j]=Domination(v1,inds[j].Y) == DominationTypes.Dominate)
                    {
                        S[j]++;
                        Q[i]++;
                    }
                }
            }
            double r = double.NegativeInfinity;
            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    if (bs[i, j])
                        R[i] += Evaluation.Distance(inds[i].Y, inds[j].Y)*(Q[j]+1) / (S[j]+1);
                    else if(bs[j,i])
                        R[i] += Evaluation.Distance(inds[i].Y, inds[j].Y) * GradientFunctions.Factorial(Q[j]);
                }
                Individual ind = inds[i];
                double d;
                if (!Memory.TryGetValue(ind, out d))
                {
                    if(R[i] == 0)
                        continue;
                    d = 0;
                }
                Memory[ind] = d + R[i];
                R[i] = (d + R[i]) / (ind.GenerationNumber + 1);
                if (R[i] > r)
                    r = R[i];
            }
            for (int i = 0; i < n; i++)
            {
                R[i] = S[i] * (r + 1) - R[i];
            }
            Array.Sort(R, list);
            Individual[] ret = new Individual[limit];
            //for(int i=0, j=0;i<n &&j<limit; i++)
            //{
            //    if(R[i]>0 || i>limit/2)
            //    {
            //        ret[j++] = list[i];
            //    }
            //}
            Array.Copy(list, 0, ret, 0, limit/2);
            Individual[] remain = new Individual[n - limit / 2];
            Array.Copy(list, limit / 2, remain, 0, remain.Length);
            NondominatedSortingSelection(remain, limit - limit / 2).CopyTo(ret, limit / 2);
            return ret;
        }
    }
}
